package com.whately.core.redis.utils;

import com.alibaba.fastjson.JSON;
import com.whately.core.base.exception.ServiceException;
import com.whately.core.base.helper.CacheHelper;
import com.whately.core.base.model.RedisKeyBuilder;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.TimeUnit;

@Component
public class RedisCacheStringUtils  {

    private final Logger logger = LoggerFactory.getLogger(RedisCacheStringUtils.class);

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedisTemplateUtils redisTemplateUtils;
    /**************************************************************************
     *     string
     *************************************************************************/

    /**
     * 取值
     *
     * @param cacheKey
     */
    public String get(RedisKeyBuilder cacheKey) {
        return get(cacheKey,String.class);
    }


    /**
     * 取值
     *
     * @param cacheKey
     * @param clazz
     */
    public <T> T get(RedisKeyBuilder cacheKey, Class<T> clazz) {
        if(java.util.List.class.isAssignableFrom(clazz)){
            throw new ServiceException(400,"get cache not allow for list,use getForList");
        }else if(java.util.Map.class.isAssignableFrom(clazz)){
            throw new ServiceException(400,"get cache not allow for map,use getForMap");
        }else{
            try {
                String value = redisTemplate.opsForValue().get(cacheKey.getKey());
                return CacheHelper.string2Value(value, clazz);
            } catch (Exception e) {
                logger.warn("Redis exception. value redis get,key {}, error msg is {}", cacheKey, e);
            }
            return null;
        }
    }

    /**
     * 取值
     *
     * @param cacheKey
     */
    public <T> List<T> getForList(RedisKeyBuilder cacheKey, Class<T> cls) {
        try {
            String value = redisTemplate.opsForValue().get(cacheKey.getKey());
            if(StringUtils.isNotBlank(value)){
                //return GsonUtils.jsonToList(value,cls);
                return JSON.parseArray(value,cls);
            }
        } catch (Exception e) {
            logger.warn("Redis exception. value redis get,key {},error msg is {}", cacheKey, e);
        }
        return null;
    }


    /**
     * 设置值
     *
     * @param cacheKey
     * @param value
     * @param <T>
     */
    public <T> void set(RedisKeyBuilder cacheKey, T value) {
        logger.debug("Enter set valueOperation redis value.key is {},value is {}", cacheKey, value);
        try {
            String v = CacheHelper.value2String(value);
            redisTemplate.opsForValue().set(cacheKey.getKey(), v, cacheKey.getDefaultExpireTime(), cacheKey.getTimeUnit());
        } catch (Exception e) {
            logger.warn("Redis exception. value redis set,key {},value is {}, error msg is {}", cacheKey, value, e);
        }
    }

    /**
     * 设置值
     *
     * @param cacheKey
     * @param value
     * @param timeout
     * @param unit
     * @param <T>
     */
    public <T> void set(RedisKeyBuilder cacheKey, T value, long timeout, TimeUnit unit) {
        logger.debug("Enter set valueOperation redis value.key is {},value is {}", cacheKey, value);
        try {
            String v = CacheHelper.value2String(value);
            redisTemplate.opsForValue().set(cacheKey.getKey(), v, timeout, unit);
        } catch (Exception e) {
            logger.warn("Redis exception. value redis set,key {},value is {}, error msg is {}", cacheKey, value, e);
        }
    }

    public <T> boolean setIfAbsent(RedisKeyBuilder cacheKey, T value) {
        logger.debug("Enter setIfAbsent valueOperation redis value.key is {},value is {}", cacheKey, value);
        boolean success=false;
        try {
            String v = CacheHelper.value2String(value);
            success = redisTemplate.opsForValue().setIfAbsent(cacheKey.getKey(), v);
        } catch (Exception e) {
            logger.warn("Redis exception. value redis set,key {},value is {}, error msg is {}", cacheKey, value, e);
        }
        return success;
    }

    public <T> boolean setNx(RedisKeyBuilder cacheKey, T value, long timeout, TimeUnit unit) {
        logger.debug("Enter set nx valueOperation redis value.key is {},value is {}", cacheKey, value);
        boolean success=false;
        try {
            String v = CacheHelper.value2String(value);
            success = redisTemplate.opsForValue().setIfAbsent(cacheKey.getKey(), v);
            if(success){
                redisTemplate.expire(cacheKey.getKey(), timeout, unit);
            }
        } catch (Exception e) {
            logger.warn("Redis exception. value redis set,key {},value is {}, error msg is {}", cacheKey, value, e);
        }
        return success;
    }

    public <T> void setWhenNullAsEmptyString(RedisKeyBuilder cacheKey, T value, long timeout, TimeUnit unit) {
        logger.debug("Enter set valueOperation redis value.key is {},value is {}", cacheKey, value);
        try {
            String v;
            if(value==null){
                v = StringUtils.EMPTY;
            }else{
                v = CacheHelper.value2String(value);
            }

            redisTemplate.opsForValue().set(cacheKey.getKey(), v, timeout, unit);
        } catch (Exception e) {
            logger.warn("Redis exception. value redis set,key {},value is {}, error msg is {}", cacheKey, value, e);
        }
    }

    /**
     * 递增或者递减
     * @param cacheKey
     * @param delta
     * @param timeout
     * @param unit
     * @return
     */
    public Long incrementWithExpire(RedisKeyBuilder cacheKey, long delta , long timeout, TimeUnit unit) {
        logger.debug("Enter increment valueOperation redis.key is {},delta is {}", cacheKey, delta);
        try {
            Long num =  redisTemplate.opsForValue().increment(cacheKey.getKey(), delta);
            if(-1==redisTemplateUtils.ttl(cacheKey)){
                redisTemplate.expire(cacheKey.getKey(), timeout, unit);
            }
            return num;
        } catch (Exception e) {
            try{
                redisTemplate.expire(cacheKey.getKey(), timeout, unit);
            }catch (Exception e1){
                logger.error("RedisValueHelper expire redis again error. with key={}", cacheKey, e1);
            }
            logger.warn("Redis exception. increment fail,key {},delta is {}, error msg is {}", cacheKey, delta, e);
        }
        return 0L;
    }

    /**
     * 失效key或者移除
     * @param cacheKey
     */
    public void delete(RedisKeyBuilder cacheKey){
        try {
            redisTemplate.delete(cacheKey.getKey());
        } catch (Exception e) {
            logger.error("Redis exception. delete key error",e);
        }
    }

}
